/*
 *  Copyright 2001-2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package ar.uba.fi.posgrado.common.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Expression;

/**
 * Builder de {@link Expression}s y {@link Criterion}s para aplicar a 
 * un criteria de hibernate.<br>
 * 
 * @author nicolas.gonzalez
 */
public class CriterionBuilder {
    
    /**
     * Lista de {@link ExpressionObj}
     */
    private List<ExpressionObj> singleExpressions = new ArrayList<ExpressionObj>();
    
    /**
     * Lista de {@link Criterion}. Se usa para ir almacenando criterios mas complejos como ser ORs, ANDs
     */
    private List<Criterion> criterions = new ArrayList<Criterion>();
    
    public CriterionBuilder() {
    }

    /**
     * @return Devuelve una {@link List lista} de {@link Criterion}s 
     */
    public List<Criterion> toCriterions() {
        List<Criterion> response = new ArrayList<Criterion>();
        
        for (ExpressionObj expObj : this.singleExpressions) {
            response.add(expObj.asSimpleExpression());
        }
        response.addAll(this.criterions);
        
        return response;
    }
    
    /**
     * Agrega otro builder al builder existente.<br>
     * @param other
     * @return
     */
    public CriterionBuilder add(CriterionBuilder other) {
        this.singleExpressions.addAll(other.getSingleExpressions());
        this.criterions.addAll(other.getCriterions());
        
        return this;
    }

    
    /**
     * Agrega una condicion de IN con los valores recibidos por parametro.<br>
     * @param property
     * @param values
     * @return
     */
    @SuppressWarnings("unchecked")
    public CriterionBuilder addIn(String property, Collection values) {
        Criterion in = Expression.in(property, values);
        this.addCriterion(in);
        
        return this;
    }
    
    /**
     * Agrega un criterio de OR usando Equals como comparacion con los valores
     * @param property
     * @param value1
     * @param value2
     * @return
     */
    public CriterionBuilder addOr(String property, Object value1, Object value2) {
        return this.addOr(property, value1, value2, ExpressionOperator.Equals);
    }
    
    /**
     * Agrega un criterio de OR usando operator como comparacion con los valores
     * 
     * @param property
     * @param value1
     * @param value2
     * @param operator
     * @return
     */
    public CriterionBuilder addOr(String property, Object value1, Object value2, ExpressionOperator operator) {
        Criterion or = Expression.or(
                            operator.asSimpleExpression(property, value1),
                            operator.asSimpleExpression(property, value2)
                                    );
        this.addCriterion(or);
        return this;
    }
    
    
    
    
    private void addCriterion(Criterion criterion) {
        if (this.criterions == null) {
            this.criterions = new ArrayList<Criterion>();
        }
        this.criterions.add(criterion);
    }
    
    
    /**
     * Agrega una expression con property, value y operador.<br>
     * @param property
     * @param value
     * @return
     */
    public CriterionBuilder add(String property, Object value, ExpressionOperator operator) {
        ExpressionObj obj = new ExpressionObj(property, value, operator);
        
        return this.add(obj);
    }
    

    /**
     * Agrega una expression con property y value. El operador es
     * {@link ExpressionOperator#Equals} por defecto.<br>
     * @param property
     * @param value
     * @return
     */
    public CriterionBuilder add(String property, Object value) {
        ExpressionObj obj = new ExpressionObj(property, value);
        
        return this.add(obj);
    }
    
    /**
     * Agrega una expresion.<br>
     * @param expressionObj
     * @return
     */
    public CriterionBuilder add(ExpressionObj expressionObj) {
        if (this.singleExpressions == null) {
            this.singleExpressions = new ArrayList<ExpressionObj>();
        }
        this.singleExpressions.add(expressionObj);
        
        return this;
    }
    
    /**
     * Agrega una lista de singleExpressions.<br>
     * @param singleExpressions
     * @return
     */
    public CriterionBuilder add(List<ExpressionObj> expressions) {
        if (this.singleExpressions == null) {
            this.singleExpressions = new ArrayList<ExpressionObj>();
        }
        this.singleExpressions.addAll(expressions);
        
        return this;
    }
    
    

    /**
     * @return the singleExpressions
     */
    protected List<ExpressionObj> getSingleExpressions() {
        return this.singleExpressions;
    }

    /**
     * @return the criterions
     */
    protected List<Criterion> getCriterions() {
        return this.criterions;
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE);
    }
    
}
